﻿using System;
using System.Linq.Expressions;
using System.Reflection;
using ApiGateway;
using ApiGateway.Attr;
using HttpApiGateway.ActionFun;
using HttpApiGateway.Interface;
using HttpApiGateway.Model;
using RpcHelper;

namespace HttpApiGateway.Helper
{
    internal delegate void ExecAction(object target, object[] args);
    internal delegate object ExecFunc(object target, object[] args);

    internal delegate IResponse ExecResponse(object targer, object[] args);
    internal class ApiRouteHelper
    {
        public static ExecResponse GetExecResponse(MethodInfo method)
        {
            ParameterExpression target = Expression.Parameter(PublicDataDic.ObjectType, "target");
            ParameterExpression arrays = Expression.Parameter(typeof(object[]), "args");
            ParameterInfo[] parameters = method.GetParameters();
            Expression[] list = new Expression[parameters.Length];
            for (int i = 0; i < parameters.Length; i++)
            {
                list[i] = Expression.Convert(Expression.ArrayIndex(arrays, Expression.Constant(i)), parameters[i].ParameterType);
            }
            if (method.IsStatic)
            {
                MethodCallExpression methodExp = Expression.Call(method, list);
                return Expression.Lambda<ExecResponse>(Expression.Convert(methodExp, PublicDict.ResponseType), target, arrays).Compile();
            }
            else
            {
                Expression exception = Expression.Convert(target, method.DeclaringType);
                MethodCallExpression methodExp = Expression.Call(exception, method, list);
                return Expression.Lambda<ExecResponse>(Expression.Convert(methodExp, PublicDict.ResponseType), target, arrays).Compile();
            }
        }
        public static ExecAction GetExecAction(MethodInfo method)
        {
            ParameterExpression target = Expression.Parameter(PublicDataDic.ObjectType, "target");
            ParameterExpression arrays = Expression.Parameter(typeof(object[]), "args");
            ParameterInfo[] parameters = method.GetParameters();
            Expression[] list = new Expression[parameters.Length];
            for (int i = 0; i < parameters.Length; i++)
            {
                list[i] = Expression.Convert(Expression.ArrayIndex(arrays, Expression.Constant(i)), parameters[i].ParameterType);
            }
            if (method.IsStatic)
            {
                MethodCallExpression methodExp = Expression.Call(method, list);
                return Expression.Lambda<ExecAction>(methodExp, target, arrays).Compile();
            }
            else
            {
                Expression exception = Expression.Convert(target, method.DeclaringType);
                MethodCallExpression methodExp = Expression.Call(exception, method, list);
                return Expression.Lambda<ExecAction>(methodExp, target, arrays).Compile();
            }
        }
        public static ExecFunc GetExecFunc(MethodInfo method)
        {
            ParameterExpression target = Expression.Parameter(PublicDataDic.ObjectType, "target");
            ParameterExpression arrays = Expression.Parameter(typeof(object[]), "args");
            ParameterInfo[] parameters = method.GetParameters();
            Expression[] list = new Expression[parameters.Length];
            for (int i = 0; i < parameters.Length; i++)
            {
                list[i] = Expression.Convert(Expression.ArrayIndex(arrays, Expression.Constant(i)), parameters[i].ParameterType);
            }
            if (method.IsStatic)
            {
                MethodCallExpression methodExp = Expression.Call(method, list);
                return Expression.Lambda<ExecFunc>(Expression.Convert(methodExp, PublicDataDic.ObjectType), target, arrays).Compile();
            }
            else
            {
                Expression exception = Expression.Convert(target, method.DeclaringType);
                MethodCallExpression methodExp = Expression.Call(exception, method, list);
                return Expression.Lambda<ExecFunc>(Expression.Convert(methodExp, PublicDataDic.ObjectType), target, arrays).Compile();
            }
        }

        private static bool _CheckIsResponseFun(MethodInfo method)
        {
            Type type = method.ReturnType;
            if (type == PublicDataDic.VoidType)
            {
                return false;
            }
            else if (type == PublicDict.ResponseType)
            {
                return true;
            }
            return type.GetInterface(PublicDict.ResponseType.FullName) != null;
        }
        public static bool CheckMethod(MethodInfo method)
        {
            ParameterInfo[] param = method.GetParameters();
            if (param.Length == 0)
            {
                return true;
            }
            return !param.IsExists(a => a.IsOut || a.ParameterType.IsByRef);
        }
        public static IHttpApi GetApi(ApiModel model,ref ApiType type)
        {
            MethodInfo method = model.Method;
            if (_CheckIsResponseFun(method))
            {
                type = ApiType.数据流;
                return new ResponseApi(model);
            }
            else if (method.ReturnType == PublicDataDic.VoidType)
            {
               return new VoidFuncApi(model);
            }
            else
            {
                return new ReturnFuncApi(model);
            }
        }
        public static ApiModel GetApiParam(MethodInfo method, RouteConfig config, IApiModular modular)
        {
            ApiModel model = new ApiModel
            {
                Method = method,
                Type = config.Type,
                Prower = config.Prower,
                IsAccredit = config.IsAccredit,
                ApiType = ApiType.API接口,
                ApiUri = ApiGateway.Helper.ApiHelper.GetApiPath(config, modular, method, out bool isRegex),
                IsRegex = isRegex
            };
            ApiPrower attr = (ApiPrower)method.GetCustomAttribute(ApiPublicDict.ProwerAttr);
            if (attr != null)
            {
                model.IsAccredit = attr.IsAccredit;
                model.Prower = attr.Prower;
            }
            ApiUpConfig upConfig = (ApiUpConfig)method.GetCustomAttribute(ApiPublicDict.ApiUpConfig);
            if (upConfig != null)
            {
                model.UpCheck = upConfig;
                model.ApiType = ApiType.文件上传;
            }
            else
            {
                model.UpCheck = ApiGatewayService.Config.UpConfig;
            }
            return model;
        }
        public static ApiModel GetApiParam(RouteConfig config, IApiModular modular)
        {
            ApiModel model = new ApiModel
            {
                Type = config.Type,
                Prower = config.Prower,
                IsAccredit = config.IsAccredit,
                ApiType = ApiType.文件上传,
                ApiUri = ApiGateway.Helper.ApiHelper.GetApiPath(config, modular, out bool isRegex),
                IsRegex = isRegex
            };
            ApiUpConfig upConfig = (ApiUpConfig)config.Type.GetCustomAttribute(ApiPublicDict.ApiUpConfig);
            if (upConfig != null)
            {
                model.UpCheck = upConfig;
            }
            else
            {
                model.UpCheck = ApiGatewayService.Config.UpConfig;
            }
            return model;
        }
        public static RouteConfig GetDefRouteConfig(Type type, IApiModular modular)
        {
            ApiPrower prower = (ApiPrower)type.GetCustomAttribute(ApiPublicDict.ProwerAttr);
            ApiRouteName route = (ApiRouteName)type.GetCustomAttribute(ApiPublicDict.ApiRouteName);
            RouteConfig config = new RouteConfig
            {
                Type = type,
                Route = route,
                IsAccredit = true,
            };
            if (prower != null)
            {
                config.IsAccredit = prower.IsAccredit;
                config.Prower = prower.Prower;
            }
            else
            {
                config.IsAccredit = modular.Config.IsAccredit;
            }
            return config;
        }
        public static string GetMethodKey(MethodInfo method)
        {
            return string.Concat(method.DeclaringType.FullName, ".", method.ToString()).GetMd5();
        }
    }
}
